package datasource

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

type delayData struct {
	ConnectorCommon
	Delay string `json:"delay"`
	DropRate uint8 `json:"drop_rate"`
	DelayBzx float64 `json:"delay_bzx"`
	Token string `json:"token"`
}

type IcmpTTL struct{
	Data []float64
}

func (i *IcmpTTL) Get(idx int) float64 {
	if idx < 0 || idx >= len(i.Data) {
		return 0
	}
	return i.Data[idx]
}

func (i *IcmpTTL) Len() int {
	return len(i.Data)
}

type ConnectorDelay struct {
	mu sync.Mutex
	data delayData
}

var gPointDelay *ConnectorDelay
var onceDelay sync.Once
const (
	DelayInterval = 3
	DelayTmpfileMax = 1000
)

func (s *ConnectorDelay) Collect()  interface{} {
	// 收集数据

	// 获取时延数据
	//ttls := s.GetDelayData(10)
	//fmt.Println("ttls : ", ttls)
	//bzx := math.Sqrt(stat.Variance(&IcmpTTL{Data: ttls}))

	// 填充连接器基本信息
	GetBaseInfo()
	basInfo := GBaseInfo
	basInfo.MessageType = MsgDelay
	basInfo.ConditionCode = fmt.Sprintf("%d-%d-%s", 12, 2, "192.168.88.88")
	data := delayData{}
	data.ConnectorCommon = basInfo

	// 填充连接器时延信息
	data.Delay = "123"
	data.DropRate = 0
	data.DelayBzx = 0

	return data
}

func (s *ConnectorDelay) DataConvert(input interface{}) []byte {
	data, ok := input.(delayData)
	if !ok {
		return nil
	}

	// 填充token
	data.Token = GLoginRsp.Token

	js, err := json.Marshal(data)
	if err != nil {
		fmt.Printf("[point] convert failed : %v\n", err)
	}
	return js
}

func (s *ConnectorDelay) DataReport(msg []byte) bool {
	if GPointFactory.config.PointDelayUrl == "" || GPointFactory.config.PointClient == nil {
		fmt.Printf("[point] [delay] report failed invalid args\n")
		return false
	}
	if GLoginRsp.Token == "" {
		fmt.Printf("[point] [delay] can not get token\n")
		return false
	}

	// 构造请求
	//fmt.Printf("[point] [delay] url : %s\n", GPointFactory.config.PointDelayUrl)
	//fmt.Printf("[point] [delay] report msg : %s\n", string(msg))
	bodyBuf := bytes.NewBuffer(msg)
	req, err := http.NewRequest(HttpPost, GPointFactory.config.PointDelayUrl, bodyBuf)
	if err != nil {
		fmt.Printf("[point] status new report failed : %v\n", err)
		return false
	}
	req.Header.Set("Content-Type", "application/json")

	// 发送请求
	rsp, err := GPointFactory.config.PointClient.Do(req)
	if err != nil {
		fmt.Printf("[point] [delay] send report failed : %v\n", err)
		return false
	}
	defer rsp.Body.Close()

	if rsp != nil {
		fmt.Println("rsp code   : ", rsp.StatusCode)
	}
	return true
}

func (s *ConnectorDelay) GetDelay() {
	for {
		icmpStr, err := exec.Command("ping", "www.baidu.com", "-c", "1", "-W", "3").Output()
		if err != nil {
			fmt.Println(err)
			return
		}
		delayNum := ParseDelayFromIcmp(string(icmpStr))
		fmt.Println(delayNum)

		// 保存时延信息
		//s.SaveDelayData(delayNum)
		time.Sleep(time.Duration(DelayInterval) * time.Second)
	}
}

func ParseDelayFromIcmp(src string) float64 {
	//解释正则表达式
	reg := regexp.MustCompile(`time=\d+\.\d+`)
	if reg == nil {
		fmt.Println("MustCompile err")
		return -1
	}
	//提取关键信息
	result := reg.FindString(src)
	arr := strings.Split(result, "=")
	if len(arr) == 2 {
		delayStr := arr[1]
		delayNum, err := strconv.ParseFloat(delayStr, 64)
		if err != nil {
			fmt.Println(err)
		}
		return delayNum
	}
	return -1
}

//func (s *ConnectorDelay) SaveDelayData(ttl float64) {
//	s.mu.Lock()
//	defer s.mu.Unlock()
//	s.data.DelayArray = append(s.data.DelayArray, ttl) // 替换旧数据
//	if len(s.data.DelayArray) > DelayTmpfileMax {
//		s.data.DelayArray = s.data.DelayArray[800:] // 保留最近200条数据
//	}
//}
//
//func (s *ConnectorDelay) GetDelayData(n int) []float64 {
//	s.mu.Lock()
//	defer s.mu.Unlock()
//	if n < 0 {
//		return nil
//	} else if n < len(s.data.DelayArray) {
//		// 返回最近的n条
//		return s.data.DelayArray[len(s.data.DelayArray) - n:]
//	} else {
//		// 不足n条数据，返回所有采样ttl
//		return s.data.DelayArray
//	}
//}

func (s *ConnectorDelay) Work() {
	fmt.Printf("[point] delay run...\n")
	// 启动时延收集协程
	go s.GetDelay()

	for {
		// 数据收集
		data := s.Collect()
		// 转换
		msg := s.DataConvert(data)
		// 上报
		s.DataReport(msg)
		time.Sleep(time.Duration(GPointFactory.config.PointInterval) * time.Second)
	}
}

func CreatePointDelay() (bool, *ConnectorDelay) {
	// 已经创建过实例，直接返回
	if gPointDelay != nil {
		return true, gPointDelay
	}
	ok := false
	onceDelay.Do(func() {
		gPointDelay = &ConnectorDelay{}
		ok = true
		fmt.Printf("[point] delay init success\n")
	})
	return ok, gPointDelay
}
